package advanced.filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

import JaCoP.constraints.Diff2;
import JaCoP.constraints.Max;
import JaCoP.constraints.XgteqY;
import JaCoP.core.IntVar;
import JaCoP.core.Store;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleSelect;
import JaCoP.search.SmallestDomain;

public class AutoRegressionFilter {


	private static Store store = new Store();
	private static HashMap<String, Operation> operations;
	private static int noOperators;
	private static Operator[] operators;
	private static int noOperations;
	private static IntVar time;
	private static IntVar[] allOrigins;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		long T1, T2, T;
		T1 = System.currentTimeMillis();
		System.out.println("Parsing data...");
		parse();
		System.out.println("Generating constraints...");
		generateConstraints();
		System.out.println("Performing search...");
		doSearch();
		T2 = System.currentTimeMillis();
		T = T2 - T1;
		System.out.println("\n\t*** Execution time = " + T + " ms");
	}

	private static void doSearch() {
		Search<IntVar> label = new DepthFirstSearch<IntVar>();

		SelectChoicePoint<IntVar> select = new SimpleSelect<IntVar>(allOrigins,
				new SmallestDomain<IntVar>(), new IndomainMin<IntVar>());
		boolean Result = label.labeling(store, select, time);

		if (Result) {
			for(String key : operations.keySet()) {
				System.out.println(operations.get(key));
			}
			System.out.println();
			System.out.println("Minimum time for schedule is " + time.value());
		} else {
			System.out.println("\n*** No");
		}
		
	}

	private static void generateConstraints() {
		ArrayList<IntVar> out = new ArrayList<IntVar>();
		allOrigins = new IntVar[noOperations];
		int n = 0;
		for (String key : operations.keySet()) {
			Operation current = operations.get(key);
			allOrigins[n] = current.getOrigin();
			n++;
			if (current.isOut()) {
				out.add(current.getEnd());
			}
			for (Operation o : current.getDependencies()) {
				store.impose(new XgteqY(current.getOrigin(), o.getEnd()));
			}
		}

		IntVar[] outs = new IntVar[out.size()];
		outs = out.toArray(outs);

		IntVar one = new IntVar(store, "one", 1, 1);

		HashMap<String, Integer> O2s = new HashMap<String, Integer>();
		HashMap<String, Integer> E2s = new HashMap<String, Integer>();
		HashMap<String, IntVar> L1s = new HashMap<String, IntVar>();
		int level = -operators[0].getAvailable();
		for (int i = 0; i < noOperators; i++) {

			String type = operators[i].getType();
			L1s.put(type,
					new IntVar(store, type + "length", operators[i].getTime(),
							operators[i].getTime()));
			O2s.put(type, level += operators[i].getAvailable());
			E2s.put(type, O2s.get(type) + operators[i].getAvailable() - 1);
		}

		IntVar[][] rectangles = new IntVar[noOperations][4];

		n = 0;
		for (String key : operations.keySet()) {
			Operation current = operations.get(key);
			String type = current.getType();
			rectangles[n] = new IntVar[] {
			current.getOrigin(),
					new IntVar(store, "", O2s.get(type), E2s.get(type)),
					L1s.get(type), one}; 
			n++;
		}
		
		store.impose(new Diff2(rectangles));
		
		
//		ArrayList<IntVar> starts = new ArrayList<IntVar>();
//		ArrayList<IntVar> durations = new ArrayList<IntVar>();
//		ArrayList<IntVar> resources = new ArrayList<IntVar>();
//		IntVar limit = new IntVar(store, "", 1, operators[1].getAvailable());
//		
//		
//		for (String key : operations.keySet()) {
//			Operation current = operations.get(key);
//			if(current.getType().equals("mul")) {
//				starts.add(current.getOrigin());
//				durations.add(current.getDuration());
//				resources.add(one);
//			}
//		}
//		
//		
//		store.impose(new Cumulative(starts, durations, resources, limit));
//		
//		
//		
//		starts = new ArrayList<IntVar>();
//		durations = new ArrayList<IntVar>();
//		resources = new ArrayList<IntVar>();
//		limit = new IntVar(store, "", 1, operators[0].getAvailable());
//		
//		
//		
//		
//		
//		for (String key : operations.keySet()) {
//			Operation current = operations.get(key);
//			if(current.getType().equals("add")) {
//				starts.add(current.getOrigin());
//				durations.add(current.getDuration());
//				resources.add(one);
//			}
//		}
//		
//		store.impose(new Cumulative(starts, durations, resources, limit));
		
		time = new IntVar(store, "time", 0, getMaxValue(operators,noOperations));
		store.impose(new Max(outs, time));
	}

	private static void parse() {
		Scanner s = null;
		try {
			s = new Scanner(new File("data.txt"));
		} catch (FileNotFoundException e) {
			System.err.println("File not found!");
			System.exit(1);
		}
		String nextLine;
		String[] splitted;
		while ((nextLine = s.nextLine()).startsWith("#"))
			;
		noOperators = Integer.parseInt(nextLine);
		operators = new Operator[noOperators];
		s.nextLine();
		for (int i = 0; i < noOperators; i++) {
			nextLine = s.nextLine();
			splitted = nextLine.split(",");
			operators[i] = new Operator(splitted[0],
					Integer.parseInt(splitted[1]),
					Integer.parseInt(splitted[2]));
		}
		s.nextLine();
		noOperations = Integer.parseInt(s.nextLine());
		s.nextLine();
		operations = new HashMap<String, Operation>();
		int maxValue = getMaxValue(operators, noOperations);
		for (int i = 0; i < noOperations; i++) {
			nextLine = s.nextLine();
			splitted = nextLine.split(",");
			operations.put(
					splitted[0],
					new Operation(store, splitted[1], Integer
							.parseInt(splitted[0]), getDuration(operators,
							splitted[1]), maxValue));
		}
		s.nextLine();
		while (s.hasNext()) {
			splitted = s.nextLine().split(":");
			Operation current = operations.get(splitted[0]);
			String[] splitted2 = splitted[1].split(",");
			for (String split : splitted2) {
				Operation o = operations.get(split);
				o.setDependent();
				current.addDependency(o);
			}
		}
		
		

	}

	private static int getDuration(Operator[] operators, String type) {
		for (Operator o : operators) {
			if (o.getType().equals(type)) {
				return o.getTime();
			}
		}
		return 0;
	}

	private static int getMaxValue(Operator[] operators, int noOperations) {
		int max = 0;
		for (Operator o : operators) {
			if (o.getTime() > max) {
				max = o.getTime();
			}
		}
		return max * noOperations;
		
	}
}
